#include"Differ.h"

node_t* copyTree(node_t* n)
{
    node_t* val = NULL;
    if (n == NULL) return NULL;
    if(n->nType == NUMBER)
        return nodeNew(n->value, copyTree(n->l), copyTree(n->r));
    return  nodeNew(n->nType, n->name, copyTree(n->l), copyTree(n->r));
}
node_t* differ(node_t* n, char arg[])
{
    printf("%p", n);
    if((n->nType == VARIABLE && strcmp(n->name, arg) || n->nType == NUMBER) && n->l == n->r && n->l == NULL)
    {
        n->nType = NUMBER;
        n->value = 0;
        return n;
    }
    if(n->nType == VARIABLE && !strcmp(n->name, arg) && n->l == n->r && n->l == NULL)
    {
        n->nType = NUMBER;
        n->value = 1;
        return n;
    }
    //if(n->nType == MOV &&)
    node_t* R_cp = NULL;
    node_t* L_cp = NULL;
    node_t* R    = NULL;
    node_t* L    = NULL;

    if(n->nType == MUL)
    {
        R_cp = copyTree(n->r);
        L_cp = copyTree(n->l);
        node_t* R = n->r;
        node_t* L = n->l;
        free(n);

        n = nodeNew(PLUS, "", nodeNew(MUL, "", differ(L_cp, arg), R), nodeNew(MUL, "", L, differ(R_cp, arg)));
    }
    else if(n->nType == MOV)
    {
        n->l = differ(n->l, arg);
        n->r = differ(n->r, arg);
    }
    else if(n->nType == PLUS || n->nType == SUB)
    {
        n->l = differ(n->l, arg);
        n->r = differ(n->r, arg);
    }
    else if(n->nType == COS || n->nType == SIN)
    {
        R_cp = copyTree(n->r);
        if(n->nType == COS)
        {
            n->nType = SIN;
            strcpy(n->name, "sin");
            n = nodeNew(MUL, "", n, nodeNew(-1, NULL, NULL));
            n = nodeNew(MUL, "", n, differ(R_cp, arg));
        }
        if(n->nType == SIN)
        {
            n->nType = COS;
            strcpy(n->name, "cos");
            n = nodeNew(MUL, "", n, differ(R_cp, arg));
        }
    }

    else if(n->nType == DIV)
    {
        R_cp = copyTree(n->r);
        L_cp = copyTree(n->l);
        R    = copyTree(n->r);

        n->l = nodeNew(SUB, "", nodeNew(MUL, "", differ(L_cp, arg), R), nodeNew(MUL, "", n->l, differ(R_cp, arg)));

        R    = copyTree(n->r);
        n->r = nodeNew(MUL, "", n->r, R);
    }
    else if(n->nType == LOG)
    {
        R_cp = copyTree(n->r);
        R    = n->r;

        free(n);
        n = nodeNew(DIV, "", nodeNew(1, NULL, NULL), R);
        n = nodeNew(MUL, "", n, differ(R_cp, arg));
    }
    else if(n->nType == EXP)
    {
        R_cp = copyTree(n->r);
        n = nodeNew(MUL, "", n, differ(R_cp, arg));
    }
    else if(n->nType == DEGREE)
    {
        n = nodeNew(MUL, "", nodeNew(n->r->value, NULL, NULL), n);
        n->r->r->value--;
    }
    return n;
}

int optimizeTree(node_t* n, FILE* f, FILE* f_treep)
{
    if (n == NULL)
        return 0;

    node_t* root = n;

    FILE* f_phrases = NULL;
    if(!(f_phrases = fopen("phrases", "r")))
        {printf("CANNOT OPEN FILE WITH PHRASES"); return 0;}

    int amount = 0;
    char** phrases = bufferize(f_phrases, &amount);
    //dumpPreambule(f);

    while(deleteConstant(n, f, n, phrases, amount))
    {
        fprintf(f, "\n\n%s\n\nIt is derivative:", phrases[rand() % amount]);
        dumpFormula(f, root);
        printTreeP(root, f_treep);
        fprintf(f_treep, "\n");
    }
    //dumpEndOfDoc(f);
}

//changes == 1 if
int deleteConstant(node_t* n, FILE* f, node_t* root, char** phrases, int amount)
{
    assert(n);

    int changes = 0;
    if(n->nType == MUL)
    {
        if(n->l->nType == NUMBER && n->l->value == 0 ||
           n->r->nType == NUMBER && n->r->value == 0)
        {
            changes = 1;
            tree_dtor(n->l); n->l = NULL;
            tree_dtor(n->r); n->r = NULL;
            n->nType = OPERATOR;
            return changes;
        }

        if(n->l->nType == NUMBER && n->l->value == 1)
        {
            free(n->l);
            n->nType = OPERATOR;
            n->l = NULL;
            changes = 1;
        }

        if(n->r->nType == NUMBER && n->r->value == 1)
        {
            free(n->r);
            n->nType = OPERATOR;
            n->r = NULL;
            changes = 1;
        }
    }

    else if(n->nType == DIV && (n->r->value == 1 && n->r->nType == NUMBER))
    {
        changes = 1;
        free(n->r); n->r = NULL;
    }

    else if((n->nType == PLUS || n->nType == SUB) && (n->r->value == 0 && n->r->nType == NUMBER))
    {
        tree_dtor(n->r); n->r = NULL;
        n->nType = OPERATOR;
        changes = 1;
    }

    else if((n->nType == PLUS || n->nType == SUB) && (n->l->value == 0 && n->l->nType == NUMBER))
    {
        tree_dtor(n->l); n->l = NULL;
        n->nType = OPERATOR;
        changes = 1;
    }
/*
    else if(n->nType == DEGREE && n->r->value == 1)
    {
        free(n->r);
        n->nType = OPERATOR;
        changes = 1;
    }
*/
    else if(!amountOfVariables(n) && n->l != NULL && n->r != NULL)
    {
        changes = 1;
        double newValue = 0;

        newValue = calculate(n);

        tree_dtor(n->l); n->l = NULL;
        tree_dtor(n->r); n->r = NULL;
        n->nType = NUMBER;
        n->value = newValue;
    }
    /*else if(n->nType == MUL && n->r->nType ==MUL)
    {
        double val = 0;
        if()
    }*/

    if(changes && n->nType != OPERATOR)
    {
        fprintf(f, "\n\n%s\n\n", phrases[rand()%amount]);
        dumpFormula(f, root);
    }
    if(n->l != NULL)
        changes += deleteConstant(n->l, f, root, phrases, amount);
    if(n->r != NULL)
        changes += deleteConstant(n->r, f, root, phrases, amount);
//printf("%d", changes);
    return changes;
}

int amountOfVariables(node_t* n)
{
    if(n == NULL)
        return 0;
    int counter = 0;

    if(n->nType == VARIABLE)
        counter++;
    if(n->l != NULL)
        counter += amountOfVariables(n->l);
    if(n->r != NULL)
        counter += amountOfVariables(n->r);
    return counter;
}

double calculate(node_t* n)
{
    if (n == NULL)
        return 0;
    double val = 0,
           R = 0  ;
    switch(n->nType)
    {
        case PLUS:
            val = calculate(n->l) + calculate(n->r);
            break;
        case SUB:
            val = calculate(n->l) - calculate(n->r);
            break;
        case MUL:
            val = calculate(n->l) * calculate(n->r);
            break;
        case DIV:
            R = calculate(n->r);
            if(R != 0)
                val = calculate(n->l) / calculate(n->r);
            else printf("\nDivision by zero \n");
            break;
        case LOG:
            val = log(calculate(n->r));
            break;
        case SIN:
            val = sin(calculate(n->r));
            break;
        case COS:
            val = cos(calculate(n->r));
            break;
        case DEGREE:
            val = pow(calculate(n->l), calculate(n->r));
            break;
        case OPERATOR:
            if (n->l != NULL)
                val = calculate(n->l);
            else if(n->r != NULL)
                val = calculate(n->r);
            n->nType = NUMBER;
            n->value = val;
            break;
        default:
            val = n->value;
            break;
    }
    return val;
}
